﻿/**************************************************************************
创建时间:	2021/9/29
作	  者:	张存
邮 	  箱:	zhangcunliang@126.com

Copyright (c) zhcun.cn

描	述：串口通讯核心类
记	录：
    增加按缓存队列发送数据，来适配硬件底层不能处理快报文的下位机 2021.10.27
***************************************************************************/
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ZhCun.SerialIO.Event;
using ZhCun.SerialIO.Filters;

namespace ZhCun.SerialIO
{
    public class SerialCore : ISerialServer
    {
        /// <summary>
        /// 串口资源对象
        /// </summary>
        public SerialPort Serial { get; private set; }
        /// <summary>
        /// 接收数据临时缓存
        /// </summary>
        byte[] Buffer { set; get; }
        /// <summary>
        /// 串口配置参数
        /// </summary>
        public SerialOption Option { private set; get; }
        /// <summary>
        /// 当连接状态改变时触发
        /// </summary>
        public event Action<ConnectChangedEvent> ConnectChanged;
        /// <summary>
        /// 当读数据完成时触发
        /// </summary>
        public event Action<ReadWriteEvent> DataReadOver;
        /// <summary>
        /// 当下发数据时触发
        /// </summary>
        public event Action<ReadWriteEvent> DataWriteOver;
        /// <summary>
        /// 串口是否打开
        /// </summary>
        public bool IsOpen { get { return Serial?.IsOpen ?? false; } }

        protected virtual void OnConnectChanged(int status)
        {
            ConnectChanged?.Invoke(new ConnectChangedEvent
            {
                Server = this,
                Status = status
            });
        }

        protected virtual void OnDataReadOver(byte[] data, int offset, int count)
        {
            DataReadOver?.Invoke(new ReadWriteEvent
            {
                Status = 1,
                Data = data,
                Offset = offset,
                Count = count
            });
        }

        protected virtual void OnDataWriteOver(byte[] data, int offset, int count)
        {
            DataWriteOver?.Invoke(new ReadWriteEvent
            {
                Status = 2,
                Data = data,
                Offset = offset,
                Count = count
            });
        }

        /// <summary>
        /// 初始化并打开串口
        /// </summary>
        public void Start(SerialOption option)
        {          
            if (Serial == null)
            {
                Serial = new SerialPort();
                Serial.DataReceived -= Serial_DataReceived;
                Serial.DataReceived += Serial_DataReceived;
            }
            if (Serial.IsOpen)
            {
                return;
            }
            Option = option;
            if (Buffer == null)
            {
                Buffer = new byte[option.BufferSize];
            }
            Serial.PortName = option.PortName;
            Serial.BaudRate = option.BaudRate;
            Serial.Open();
            OnConnectChanged(1);
            InitWriteQueue();
        }
        /// <summary>
        /// 初始化并打开串口
        /// </summary>
        public void Start(string portName, int baudRate = 9600)
        {
            SerialOption option = new SerialOption
            {
                PortName = portName,
                BaudRate = baudRate
            };
            Start(option);
        }
        /// <summary>
        /// 重新启动
        /// </summary>
        public void Restart()
        {
            Dispose();
            Start(Option);
        }
        /// <summary>
        /// 释放端口
        /// </summary>
        public virtual void Dispose()
        {
            Serial.Close();
            Serial.Dispose();
            Serial = null;
            for (int i = 0; i < Buffer.Length; i++)
            {
                Buffer[i] = 0;
            }
            OnConnectChanged(2);
        }
        /// <summary>
        /// 数据接收
        /// </summary>
        private void Serial_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            int offset = 0;
            while (Serial.IsOpen && Serial.BytesToRead > 0 && offset < Buffer.Length)
            {
                if (offset + Serial.BytesToRead > Buffer.Length)
                {
                    break; //缓存数据不足，或发送数据过快，分包处理
                }
                offset += Serial.Read(Buffer, offset, Serial.BytesToRead);

                Thread.Sleep(Option.ReadInterval);
            }
            if (offset == 0) return;
            OnDataReadOver(Buffer, 0, offset);
        }

        #region 写数据的队列处理
        
        class WriteBuff
        {
            public WriteBuff(byte[] data, int offset, int count)
            {
                Data = data;
                Offset = offset;
                Count = count;
            }

            public byte[] Data { set; get; }

            public int Offset { set; get; }

            public int Count { set; get; }
        }

        /// <summary>
        /// 1级 最后发送
        /// </summary>
        Queue<WriteBuff> WriteQueueL1 { set; get; }
        /// <summary>
        /// 2级 优先1级发送
        /// </summary>
        Queue<WriteBuff> WriteQueueL2 { set; get; }
        /// <summary>
        /// 3级 优先2级发送
        /// </summary>
        Queue<WriteBuff> WriteQueueL3 { set; get; }

        void InitWriteQueue()
        {
            if (WriteQueueL1 != null) return;

            WriteQueueL1 = new Queue<WriteBuff>();
            WriteQueueL2 = new Queue<WriteBuff>();
            WriteQueueL3 = new Queue<WriteBuff>();
            Task.Factory.StartNew(RunWriteTask);
        }

        void RunWriteTask()
        {
            while (Option.WriteInterval > 0)
            {
                Thread.Sleep(Option.WriteInterval);
                if (Serial == null || Serial.IsOpen == false) continue;
                if (BufferSend(WriteQueueL3)) continue;

                if (BufferSend(WriteQueueL2)) continue;

                if (BufferSend(WriteQueueL1)) continue;
            }

            bool BufferSend(Queue<WriteBuff> queue)
            {
                if (queue?.Count > 0)
                {
                    var data = queue.Dequeue();
                    Write(data.Data, data.Offset, data.Count, 0);
                    return true;
                }
                return false;
            }

            //void QueueHandle(Queue<WriteBuff> qu)
            //{
            //    if (qu.Count > 100)
            //    {
            //        qu.Clear(); //处理不过来了，清除了
            //    }
            //}
        }
        
        #endregion

        /// <summary>
        /// 原始数据发送
        /// </summary>
        public void Write(byte[] data, int offset, int count, int bufferLevel)
        {
            if (bufferLevel == 1)
            {
                if (WriteQueueL1 == null) return;
                WriteQueueL1.Enqueue(new WriteBuff(data, offset, count));
            }
            else if (bufferLevel == 2)
            {
                if (WriteQueueL2 == null) return;
                WriteQueueL2.Enqueue(new WriteBuff(data, offset, count));
            }
            else if (bufferLevel == 3)
            {
                if (WriteQueueL3 == null) return;
                WriteQueueL3.Enqueue(new WriteBuff(data, offset, count));
            }
            else
            {
                if (Serial == null || !Serial.IsOpen) return;
                Serial.Write(data, offset, count);
                OnDataWriteOver(data, offset, count);
            }
        }
        /// <summary>
        /// 原始数据发送
        /// </summary>
        public virtual void Write(byte[] data, int offset, int count)
        {
            if (Option.WriteInterval > 0)
            {
                Write(data, offset, count, 1);
            }
            else
            {
                Write(data, offset, count, 0);
            }
        }
        /// <summary>
        /// 可指定次数的
        /// </summary>
        /// <param name="data">数据缓存</param>
        /// <param name="offset">偏移量</param>
        /// <param name="count">长度</param>
        /// <param name="sendTimes">发送次数</param>
        /// <param name="interval">间隔毫秒</param>
        public void Write(byte[] data, int offset, int count, int sendTimes, int interval)
        {
            if (sendTimes < 0) sendTimes = 1;
            if (interval < 20) interval = 20;
            for (int i = 0; i < sendTimes; i++)
            {
                Write(data, offset, count);
                Thread.Sleep(interval);
            }
        }
        /// <summary>
        /// 整个数组发送到串口
        /// </summary>
        public void Write(byte[] data)
        {
            Write(data, 0, data.Length);
        }
        /// <summary>
        /// 发送一个自定义实体的消息
        /// </summary>
        public void Write(IWriteModel model)
        {
            Write(model, 0);
        }

        public void Write(IWriteModel model, int level = 0)
        {
            var data = model.GetData();
            Write(data, 0, data.Length, level);
        }
    }
}